package com.atwisdom.star.core.bsl.planner.Impl;

import cn.hutool.core.collection.CollectionUtil;
import com.alibaba.fastjson.JSONObject;
import com.atwisdom.star.common.util.DateUtils;
import com.atwisdom.star.core.bsl.planner.EquipmentOccupyBslService;
import com.atwisdom.star.core.bsl.planner.entity.EquipmentOccupy;
import com.atwisdom.star.core.bsl.planner.entity.SaleOrder;
import com.atwisdom.star.core.bsl.planner.entity.WorkProcessPath;
import com.atwisdom.star.core.dsl.modelClass.hier.HierInstanceDslService;
import com.atwisdom.star.core.dsl.modelClass.logic.Impl.LogicInstanceDslServiceImpl;
import com.atwisdom.star.exception.pojo.BusinessException;
import lombok.Data;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.time.LocalDate;
import java.time.temporal.TemporalAdjusters;
import java.util.*;
import java.util.stream.Collectors;

@Data
@Service
public class EquipmentOccupyBslServiceImpl implements EquipmentOccupyBslService {
    @Autowired
    private LogicInstanceDslServiceImpl logicInstanceDslService;

    @Autowired
    private HierInstanceDslService hierInstanceDslService;

    @Autowired
    private SaleOrderBslServiceImpl saleOrderBslService;

    @Autowired
    private SysConfigBslServiceImpl sysConfigBslService;

    @Autowired
    private WorkProcessPathBslServiceImpl workProcessPathBslService;

    @Autowired
    private WorkCalendarBslServiceImpl workCalendarBslService;

    @Autowired
    private MrpLogBslServiceImpl mrpLogBslService;

    //设备占列表
    private List<EquipmentOccupy> equipmentOccupyList = new ArrayList<>();
    //所有设备初始占用列表
    private List<EquipmentOccupy> allEquipmentOccupyList = new ArrayList<>();
    //以日期为key,设备占用列表为value 的Map
    private Map<LocalDate,List<EquipmentOccupy>> equipmentOccupyMap = new HashMap<>();

    /**
     * 根据销售订单初始化设备占用
     */
    public void initEquipmentOccupyBySaleOrder() {
        this.equipmentOccupyList = new ArrayList<>();
        this.allEquipmentOccupyList = new ArrayList<>();
        this.initAllEquipmentOccupy();
        Date deliveryDateMax = this.saleOrderBslService.getSaleOrders().stream()
                .map(SaleOrder::getDeliveryDate)
                .max(Date::compareTo)
                .orElse(null);
        if (deliveryDateMax != null) {
            LocalDate endDate = DateUtils.ConvertDateToLocalDate(deliveryDateMax);
            LocalDate today = LocalDate.now().plusDays(-1);
            while (endDate.isAfter(today)) {
                Boolean isHoliday = this.workCalendarBslService.checkHoliday(endDate);
                if (!isHoliday) {
                    this.calculateEquipmentOccupy(endDate);
                }
                endDate = endDate.plusDays(-1);
            }
        }
    }

    /**
     * 根据内示计划初始化设备点用
     *
     * @param dataObj 内示参数
     */
    public void initEquipmentOccupyByMonthPlan(JSONObject dataObj) {
        this.equipmentOccupyList = new ArrayList<>();
        this.allEquipmentOccupyList = new ArrayList<>();
        this.initAllEquipmentOccupy();
        JSONObject param = dataObj.getJSONObject("param");
        String yearMonth = param.getString("yearMonth");
        String[] ymAry = yearMonth.split("-");
        int year = Integer.parseInt(ymAry[0]);
        int month = Integer.parseInt(ymAry[1]);
        LocalDate firstDate = LocalDate.of(year, month, 1);
        LocalDate endDate =  firstDate.with(TemporalAdjusters.lastDayOfMonth());
        LocalDate today = LocalDate.now().plusDays(-1);
        while (endDate.isAfter(today)) {
            Boolean isHoliday = this.workCalendarBslService.checkHoliday(endDate);
            if (!isHoliday) {
                this.calculateEquipmentOccupy(endDate);
            }
            endDate = endDate.plusDays(-1);
        }
    }

    private void initAllEquipmentOccupy() {
        List<JSONObject> equipJsonObject = this.logicInstanceDslService.queryJsonByClassName("EquipmentInfo");
        for (JSONObject jsonObject : equipJsonObject) {
            Long id = jsonObject.getLong("id");
            EquipmentOccupy equipmentOccupy = new EquipmentOccupy();
            JSONObject values = jsonObject.getJSONObject("values");
            String runStatus = values.getString("runStatus");
            equipmentOccupy.setId(id);
            if ("0".equalsIgnoreCase(runStatus)) {
                equipmentOccupy.setStopped(0);
                equipmentOccupy.setOccupyDuration(this.sysConfigBslService.getWorkHours());
                equipmentOccupy.setEffectiveDuration(0);
            } else {
                equipmentOccupy.setStopped(1);
                equipmentOccupy.setOccupyDuration(0);
                equipmentOccupy.setEffectiveDuration(this.sysConfigBslService.getWorkHours());
            }
            this.allEquipmentOccupyList.add(equipmentOccupy);
        }
    }

    /**
     * 计算生产设备占用分析
     *
     * @param occupyDate 占用日期
     */
    public void calculateEquipmentOccupy(LocalDate occupyDate) {
        List<EquipmentOccupy> tempEquipmentOccupyList = new ArrayList<>();
        this.allEquipmentOccupyList.forEach(eq->{
            EquipmentOccupy equipmentOccupy = new EquipmentOccupy();
            BeanUtils.copyProperties(eq,equipmentOccupy);
            equipmentOccupy.setOccupyDate(occupyDate);
            tempEquipmentOccupyList.add(equipmentOccupy);
        });

        String startDate = DateUtils.getLocalDateFormat(occupyDate, "yyyy-MM-dd 00:00:00");
        String endDate = DateUtils.getLocalDateFormat(occupyDate, "yyyy-MM-dd 23:59:59");

        //保养工单占用
        String maintainWorkOrderCondition = " maintainStartTime >='" + startDate + "' AND maintainStartTime <= '" + endDate + "' AND isApproved = 1 ";
        List<JSONObject> maintainWorkOrderJson = this.logicInstanceDslService.query(maintainWorkOrderCondition, "MaintainWorkOrder", null);
        this.calculateEquipmentOccupyPatrolOrMaintain(occupyDate, maintainWorkOrderJson, tempEquipmentOccupyList);

        //设备点巡检工单占用
        String patrolWorkOrderCondition = " patrolStartTime >='" + startDate + "' AND patrolStartTime <= '" + endDate + "' AND isApproved = 1 ";
        List<JSONObject> patrolWorkOrderJson = this.logicInstanceDslService.query(patrolWorkOrderCondition, "PatrolWorkOrder", null);
        this.calculateEquipmentOccupyPatrolOrMaintain(occupyDate, patrolWorkOrderJson, tempEquipmentOccupyList);
        //锁定生产计划订单设备占用
        String condition = " isCreate = 0 and endDate >= '" + startDate + "' and endDate <= '" + endDate + "'";
        List<JSONObject> productionOrderList = this.logicInstanceDslService.query(condition, "ProductionPlanOrder", null);
        for (JSONObject jsonObject : productionOrderList) {
            JSONObject values = jsonObject.getJSONObject("values");
            String bCode = values.getString("material_bcode");
            Double tempPlanDealDuration = values.getDouble("workHours");
            double planDealDuration;
            if (tempPlanDealDuration != null) {
                planDealDuration = tempPlanDealDuration;
            } else {
                planDealDuration = 0;
            }

            WorkProcessPath workProcessPath = this.workProcessPathBslService.fetchWorkProcessPath(bCode);
            if (workProcessPath == null) {
                String msg = String.format("背番: %s物料工艺路径没有维护，计划与排程运行失败！", bCode);
                this.mrpLogBslService.buildMrpLog(2, msg);
                throw new BusinessException("999999", msg);
            }else {
                List<Long> equipmentList = workProcessPath.getEquipmentList();
                equipmentList.forEach(eq -> {
                    EquipmentOccupy tempEquipmentOccupy = tempEquipmentOccupyList.stream().filter(teq -> teq.getId().equals(eq)).findFirst().orElse(null);
                    if (tempEquipmentOccupy != null) {
                        if (tempEquipmentOccupy.getStopped() != 0) {
                            tempEquipmentOccupyList.removeIf(teq -> teq.getId().equals(eq));
                            tempEquipmentOccupy.setId(eq);
                            tempEquipmentOccupy.setOccupyDate(occupyDate);
                            tempEquipmentOccupy.setOccupyDuration(planDealDuration + tempEquipmentOccupy.getOccupyDuration());
                            tempEquipmentOccupy.setEffectiveDuration(this.sysConfigBslService.getWorkHours() - tempEquipmentOccupy.getOccupyDuration());
                            tempEquipmentOccupyList.add(tempEquipmentOccupy);
                        }
                    }
                });
            }
        }

        List<EquipmentOccupy> theEquipmentOccupyList = tempEquipmentOccupyList.stream()
                .collect(Collectors.groupingBy(
                        EquipmentOccupy::getId
                ))
                .values()
                .stream()
                .map(group -> {
                    int stopped = group.stream()
                            .mapToInt(EquipmentOccupy::getStopped)
                            .min()
                            .orElse(1);
                    double occupyDuration = group.stream()
                            .mapToDouble(EquipmentOccupy::getOccupyDuration)
                            .sum();
                    double effectiveDuration = this.sysConfigBslService.getWorkHours()- occupyDuration;
                    if (effectiveDuration < 0) {
                        effectiveDuration = 0;
                        occupyDuration = this.sysConfigBslService.getWorkHours();
                    }
                    return new EquipmentOccupy(group.get(0).getId(), occupyDate, stopped, occupyDuration, effectiveDuration, null);
                }).collect(Collectors.toList());

        if (CollectionUtil.isEmpty(theEquipmentOccupyList)) {
            theEquipmentOccupyList = new ArrayList<>();
        }
        this.equipmentOccupyList.addAll(theEquipmentOccupyList);
        this.equipmentOccupyMap.put(occupyDate, equipmentOccupyList);
    }

    /**
     * 处理设备巡检和保养占用
     * @param occupyDate 占用日期
     * @param workOrderJson 工单json
     * @param tempEquipmentOccupyList 临时占用列表
     */
    private void calculateEquipmentOccupyPatrolOrMaintain(LocalDate occupyDate, List<JSONObject> workOrderJson, List<EquipmentOccupy> tempEquipmentOccupyList) {
        EquipmentOccupy equipmentOccupy;
        for (JSONObject jsonObject : workOrderJson) {
            JSONObject values = jsonObject.getJSONObject("values");
            Long id = values.getLong("equipList");
            Double idlingPeriod = values.getDouble("idlingPeriod");
            if(idlingPeriod==null){
                idlingPeriod =0.0;
            }

            equipmentOccupy = tempEquipmentOccupyList.stream().filter(teq -> teq.getId().equals(id)).findFirst().orElse(null);
            if(equipmentOccupy==null){
                continue;
            }
            if (equipmentOccupy.getStopped() != 0) {
                tempEquipmentOccupyList.removeIf(teq -> teq.getId().equals(id));
                equipmentOccupy.setId(id);
                equipmentOccupy.setOccupyDate(occupyDate);
                equipmentOccupy.setOccupyDuration(idlingPeriod);
                equipmentOccupy.setEffectiveDuration(this.sysConfigBslService.getWorkHours() - idlingPeriod);
                tempEquipmentOccupyList.add(equipmentOccupy);
            }
        }
    }
}
